/* hc256.c
** 
** Copyright (c) 2007, Matt Hamrick, Siobhan Murtagh & Damon Orecks
** All rights reserved.
** 
** Redistribution  and  use  in  source  and  binary  forms,  with  or  without
** modification, are permitted provided that the following conditions are met:
** 
**  * Redistributions of  source code must  retain the above  copyright notice,
**    this list of conditions and the following disclaimer.
** 
**  * Redistributions in binary form must reproduce the above copyright notice,
**    this list of conditions and the following disclaimer in the documentation
**    and/or other materials provided with the distribution.
** 
**  * Neither  the  name  of the  DangerThink  Project  nor  the names  of  its
**    contributors may be used to endorse or promote products derived from this
**    software without specific prior written permission.
** 
** THIS SOFTWARE IS PROVIDED BY  THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY  EXPRESS OR IMPLIED WARRANTIES,  INCLUDING, BUT NOT  LIMITED TO, THE
** IMPLIED WARRANTIES  OF MERCHANTABILITY AND FITNESS FOR  A PARTICULAR PURPOSE
** ARE DISCLAIMED.  IN NO  EVENT SHALL THE  COPYRIGHT OWNER OR  CONTRIBUTORS BE
** LIABLE  FOR  ANY  DIRECT,   INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR
** CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT  NOT  LIMITED  TO,  PROCUREMENT  OF
** SUBSTITUTE GOODS  OR SERVICES;  LOSS OF USE,  DATA, OR PROFITS;  OR BUSINESS
** INTERRUPTION)  HOWEVER CAUSED  AND ON  ANY THEORY  OF LIABILITY,  WHETHER IN
** CONTRACT,  STRICT LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE OR  OTHERWISE)
** ARISING IN ANY WAY  OUT OF THE USE OF THIS SOFTWARE,  EVEN IF ADVISED OF THE
** POSSIBILITY OF SUCH DAMAGE.
**
** $Id: hc256.c 21 2007-05-14 22:57:09Z msh.mobile $
*/

/* Macro Definitions */
#define DTC_KERNEL

/* File Includes */
#include "portable.h"
#include "hc256.h"

/* Static Prototypes */

/* Function Definitions */

DTErr hc256_new( hc256 **cipher ) {
  DTErr err = DT_E_NOERR;

  do {

    CFE( hc256**, cipher );

    if( NULL == *cipher ) {
      if( NULL == ( *cipher = malloc( sizeof( hc256 ) ) ) ) {
	err = DT_E_MALLOC;
	break;
      }
    }

    err = hc256_clear( *cipher );

  } while( 0 );

  return( err );
}

DTErr hc256_delete( hc256 *cipher ) {
  DTErr err = DT_E_NOERR;

  do {
    CFE( hc256 *, cipher );

    err = hc256_clear( cipher );

    free( cipher );
  } while( 0 );

  return( err );
}

DTErr hc256_key( hc256 *cipher, char *key ) {
  DTErr err = DT_E_NOERR;

  do {
    CFE( hc256 *, cipher );
    CFE( char *, key );

#ifdef WORDS_BIGENDIAN
    memcpy( cipher->key, key, 32 );
#else
 {
   uint32_t i;
   uint32_t *k = (uint32_t *) key;

   for( i = 0; i < 8; i++ ) {
     cipher->key[i] = ntohl( k[i] );
   }
 }
#endif

  } while( 0 );

  return( err );
}

DTErr hc256_iv( hc256 *cipher, char *iv ) {
  DTErr err = DT_E_NOERR;

  do {
    CFE( hc256 *, cipher );
    CFE( char *, iv );
#ifdef WORDS_BIGENDIAN
    memcpy( cipher->iv, iv, 32 );
#else
 {
   uint32_t i;
   uint32_t *k = (uint32_t *) iv;

   for( i = 0; i < 8; i++ ) {
     cipher->iv[i] = ntohl( k[i] );
   }
 }
#endif
  } while( 0 );

  return( err );
}

DTErr hc256_init( hc256 *cipher ) {
  DTErr err = DT_E_NOERR;

  do {
    CFE( hc256 *, cipher );

    {
      /* todo: change this so we can allocate off the stack or off the heap */
      uint32_t w[ 2560 ];
      hc256_lower_init( cipher, w );
    }
  } while( 0 );

  return( err );
}


DTErr hc256_keystream( hc256 *cipher, char *output, unsigned int length, unsigned int *bytes_written ) {
  DTErr err = DT_E_NOERR;

  do {
    CFE( hc256 *, cipher );
    CFE( char *, output );

    hc256_lower_keystream( cipher, (unsigned int *) output, length, bytes_written );

#ifndef WORDS_BIGENDIAN
    /* ohh... this is a temporary fix for issue 5. But we're going to have to
    ** eventually revisit it. Many platforms do not allow you to copy words
    ** on unaligned boundaries. It's entirely possible that we'll wind up with
    ** a situation where the consumer asks for an odd number of bytes at a
    ** time. This fix (simply htonl()ing the output) won't work in this case.
    */

    /* todo: add code that returns individual bytes from the context, and does
    ** it in an endian sensitive way.
    */
    
 {
   uint32_t i;
   uint32_t l = ( length >> 2 ) & 0x3FFFFFFF ;
   uint32_t *k = (uint32_t *) output;
   for( i = 0; i < l; i++ ) {
     k[i] = htonl( k[i] );
   }
 }
#endif    
  } while( 0 );

  return( err );
}

DTErr hc256_encrypt( hc256 *cipher, char *input, unsigned int input_length, char *output, unsigned int output_length, unsigned int *bytes_written ) {
  DTErr err = DT_E_NOERR;
  unsigned int bytes_to_write, i;
  char *data;
  unsigned int next_byte;

  do {
    CFE( hc256 *, cipher );
    CFE( char *, input );
    CFE( char *, output );

    if( input_length < output_length ) {
      bytes_to_write = input_length;
    } else {
      bytes_to_write = output_length;
    }

    data = (char *) cipher->data;

    for( i = 0; i < bytes_to_write; i++ ) {
      next_byte = cipher->counter % 64;
 
#ifndef WORDS_BIGENDIAN
      next_byte ^= 0x03;
#endif

      if( 0 == cipher->left ) {
	hc256_lower_advance( cipher );
      }

      output[i] = input[i] ^ data[ next_byte ];

      cipher->counter ++;
      cipher->left --;
    }

    if( NULL != bytes_written ) {
      *bytes_written = bytes_to_write;
    }

  } while( 0 );

  return( err );
}

DTErr hc256_decrypt( hc256 *cipher, char *input, unsigned int input_length, char *output, unsigned int output_length, unsigned int *bytes_written ) {
  return( hc256_encrypt( cipher, input, input_length, output, output_length, bytes_written ) );
}

DTErr hc256_seek( hc256 *cipher, DTSeekMode mode, int offset ) {
  DTErr err = DT_E_NOERR;

  do {
    CFE( hc256 *, cipher );

    if( ( mode != DT_SEEK_ABSOLUTE ) && ( mode != DT_SEEK_RELATIVE ) ) {
      err = DT_E_BADPARAMS;
      break;
    }

    if( ( mode == DT_SEEK_ABSOLUTE ) && ( offset < 0 ) ) {
      err = DT_E_BADPARAMS;
      break;
    }

    err = DT_E_UNIMPLEMENTED;

  } while( 0 );

  return( err );
}

 DTErr hc256_clear( hc256 *cipher ) {
   memset( cipher, 0, sizeof( hc256 ) );
   return( DT_E_NOERR );
 }

